/**
 * \class GenNonTerminal.cpp /GenProg/Generator/Exp/GenNonTerminal.cpp
 *
 *  \date 5 jul 2012
 *  \author Calle
 *  
 *  \brief implementation of GenNonTerminals
 */

#include <Generator/Exp/GenNonTerminal.h>
#include <cstdlib>
#include <Generator/GenExp.h>
#include <Interfaces/IGenExp.h>
#include <list>
#include <RandomPkg/Randomizer.h>

GenNonTerminal::GenNonTerminal(const ExpRegistry& registry,const TypeEnum& type,IContext* currentContext,unsigned int depth):
m_Registry(registry),
m_Type(type),
m_currentContext(currentContext),
m_Depth(depth)
{


}

GenNonTerminal::~GenNonTerminal() {

}

AbsSyn::IExpPtr GenNonTerminal::genNonTerminal()
{
	AbsSyn::IExpPtr ptr;
	ExpRegistry::TGenFunTypeMap outList;
	ExpRegistry::TGenPtr genFun;
	if( m_Registry.getAll(TerminalState::NON_TERMINAL,outList) )
	{

		//Choose one of the gen functions to create a new non-terminal
		genFun = randChooseGenExpFun(outList);

		ptr = constructExp(genFun);
		assert(ptr);

	}

	return ptr;
}

ExpRegistry::TGenPtr GenNonTerminal::randChooseGenExpFun( const ExpRegistry::TGenFunTypeMap& genMap) const
{
	GenPtrList ptrList= getListWithMatchingType(genMap);
	assert(ptrList.size() != 0); //Assert if we don't have any...

	RandomPkg::Randomizer randGen;
	/// TODO Need to verify that all values are counted int ptrList.size -1
	int choosenOrdinal = randGen.createRandom<int>(0,ptrList.size()-1);

	GenPtrList::const_iterator iter = ptrList.begin();

	while( iter != ptrList.end() && choosenOrdinal != 0)
	{
		iter++;
		choosenOrdinal--;
	}

	assert(iter != ptrList.end());
	ExpRegistry::TGenPtr genFun = (*iter);


	/// \todo needs to get a better debug for this
	//std::cout << "Choosen non-terminal " << (*iter).first.toString() << std::endl;
	return genFun;

}

AbsSyn::IExpPtr GenNonTerminal::constructExp(const ExpRegistry::TGenPtr& genFun) const
{

	GenExp::GenExpPtr genPtr( new GenExp(m_Registry,m_Type,m_currentContext,m_Depth) );
	assert(genFun);
	AbsSyn::IExpPtr expPtr = genFun->genExp(genPtr);
	assert(expPtr);
	return expPtr;
}

GenNonTerminal::GenPtrList GenNonTerminal::getListWithMatchingType(const ExpRegistry::TGenFunTypeMap & genMap) const
{
	ExpRegistry::TGenFunTypeMap::const_iterator iter = genMap.begin();

	GenPtrList ptrList;

	for(; iter != genMap.end();++iter)
	{
		if( (*iter).second->isTypeMatch(m_Type) )
		{
			ptrList.push_back((*iter).second);
		}
	}

	return ptrList;
}






